Kriticke sekce a spol.
Otázka od: Petr Selinger
5. 12. 2002 10:53
Zdravim,
netusi nekdo jak se pouzivaji spravne kriticke sekce, semafory, atd?
Resim problem pri vymene dat - odesilatel vysle zpravu,
prijemce ji zachyti a posle zpatky, odesilatel ji znovu zachyti,
zkontroluje a posle novou zpravu, atd.
Problem je, ze po urcite dobe dojde ke StackOverFlow, protoze
kdyz se procedury pro odeslani a prijem se nestihnou dokoncit, muze
dojit na zaklade vyvolani eventu k dalsim vstupum do techto
procedur.
Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
chce vstoupit do jeste nedokoncene procedury muselo pockat, nez
se procedura radne ukonci.
Neresil nekdo nekdy neco podobneho?
Diky, Petr Selinger
--------------------
Siemens C45 za 2.977 Kč. Nejvýhodnější nabídka telefonů na trhu!
http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets/handset.php
Odpovedá: Robert Suska
5. 12. 2002 11:17
Ahoj!
Ja pouzivam semafor ale taky primitivny a asi takto.
mam nejaku globalnu premennu bSemaphore: Boolean; pri initialization si ju
nastavim na True potom mam metodu
procedure Semaphore;
begin
while bSemaphore do
begin
Sleep(1);
Application.ProcessMessage;
end;
bSemaphore:= True; // znovu nastavim ze budem cakat na data
end;
procedure SendData;
begin
CLS.Socket.SendText('CMD GET COPY FILE'); // som si len vymyslel
Semaphore;
end;
procedure ReceiveData;
begin
sReceiveData:= CLS.Socket.ReceiveText;
bSemaphore:= False; // mam data a vyskocim s cyklu
end;
initialization
bSemaphore:= True;
------------
tod vsjo.
S pozdravom,
************************************
Robert Suska
KORAK SLOVAKIA s.r.o.
Horna 52
974 01 Banska Bystrica
www.korak.sk robert@korak.sk
Network administrator, Delphi programmer
************************************
> Zdravim,
> netusi nekdo jak se pouzivaji spravne kriticke sekce, semafory, atd?
>
> Resim problem pri vymene dat - odesilatel vysle zpravu,
> prijemce ji zachyti a posle zpatky, odesilatel ji znovu zachyti,
> zkontroluje a posle novou zpravu, atd.
>
> Problem je, ze po urcite dobe dojde ke StackOverFlow, protoze
> kdyz se procedury pro odeslani a prijem se nestihnou dokoncit, muze
> dojit na zaklade vyvolani eventu k dalsim vstupum do techto
> procedur.
>
> Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
> chce vstoupit do jeste nedokoncene procedury muselo pockat, nez
> se procedura radne ukonci.
>
> Neresil nekdo nekdy neco podobneho?
>
> Diky, Petr Selinger
>
> --------------------
> Siemens C45 za 2.977 Kč. Nejvýhodnější nabídka telefonů na trhu!
http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets/hands
et.php
>
>
Odpovedá: Blazek Jaroslav
5. 12. 2002 11:57
Ahoj,
> delphiforum@centrum.cz 5.12.02 10:39 >>>
> Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
> chce vstoupit do jeste nedokoncene procedury muselo pockat, nez
> se procedura radne ukonci.
http://sweb.cz/data.product/Delphi/5/Threads/Threads.zip
mas tam 2 thready, ktere mezi sebou komunikuji pomoci binarnich semaforu
S pozdravem
Bc. Jaroslav Blazek
Access-IT Ceska Lipa
mailto:jaroslav.blazek@access-it.cz
http://www.access-it.cz
ICQ# : 133673990
+420605/813644
Odpovedá: Radek KALA
5. 12. 2002 12:01
Pokud mluvis o windowssovkych zpravach, neposilas je nahodou
prikazem sendmessage ?
Mozna by stacilo dat prikaz postmessage.
> Zdravim,
> netusi nekdo jak se pouzivaji spravne kriticke sekce, semafory, atd?
>
> Resim problem pri vymene dat - odesilatel vysle zpravu,
> prijemce ji zachyti a posle zpatky, odesilatel ji znovu zachyti,
> zkontroluje a posle novou zpravu, atd.
>
> Problem je, ze po urcite dobe dojde ke StackOverFlow, protoze
> kdyz se procedury pro odeslani a prijem se nestihnou dokoncit, muze
> dojit na zaklade vyvolani eventu k dalsim vstupum do techto
> procedur.
>
> Napadlo me pouzit neco jako semafory, kdy by to vlakno, ktere
> chce vstoupit do jeste nedokoncene procedury muselo pockat, nez se
> procedura radne ukonci.
>
> Neresil nekdo nekdy neco podobneho?
>
> Diky, Petr Selinger
>
> --------------------
> Siemens C45 za 2.977 Kč. Nejvýhodnější nabídka telefonů na trhu!
> http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets
> /handset.php
>
S pozdravem Radek KALA
BetaControl, s.r.o.
Cerneho 58/60, 635 00
tlf. : + 420 5 4622 3491
fax : + 420 5 4622 3470
GSM : + 420 603 85 75 15
Odpovedá: Petr Vones
5. 12. 2002 13:47
From: "Robert Suska" <delphi@korak.sk>
> mam nejaku globalnu premennu bSemaphore: Boolean; pri initialization si ju
> nastavim na True potom mam metodu
>
> procedure Semaphore;
> begin
> while bSemaphore do
> begin
> Sleep(1);
> Application.ProcessMessage;
> end;
> bSemaphore:= True; // znovu nastavim ze budem cakat na data
> end;
Toto je primo ukazkovy priklad jak se vec rozhodne nema resit. Procedura
Semaphore se ti totiz zevnitr 'Application.ProcessMessage' muze znovu zavolat,
a to i nekolikrat, protoze porusujes zasadu kde v aplikaci ma byt pouze jedno
misto ve kterem se vyrizuji zpravy. Vetsina kodu nebyva reentrantni
K podobnym vecem prave slouzi eventy a dalsi synchronizacni objekty operacniho
systemu. Nemluve o tom, ze cekani ve smycce na nejaky flag nesmyslne zatezuje
procesor, od toho jsou prave WaitXXX funkce ktere zajisti prepnuti do kernel
modu a tim naznaci scheduleru ze nema danemu thredu pridelovat zadny cas.
Petr Vones
Odpovedá: Petr Selinger
5. 12. 2002 13:18
Pro ilustraci davam kousek kodu a vysvetlim, ceho bych chtel
dosahnout.
...
var
Form1: TForm1;
Semaphore: THandle;
...
procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
begin
WaitForSingleObject(Semaphore, INFINITE);
for i := 0 to 30000 do begin
Label1.Caption := IntToStr(i);
Application.ProcessMessages;
end;
ReleaseSemaphore(Semaphore, 1, nil);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Semaphore := CreateSemaphore(nil, 1, 1, nil);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(Semaphore);
end;
Jde mi o to, aby kdyz se jiz procedura zpracovava a zavola se znovu,
aby ke spousteni doslo az po dokonceni prvniho behu procedury.
V prikladu se to projevi tak, ze kdyz kliknu na tlacitko, spusti se
vypis cisel. Pokud pri tomto vypisu kliknu jeste jednou, chtel
bych, aby se druhy vstup do procedury pozastavil a pak az se
dokonci prvni, zacne se provadet druhy vstup.
Zatim to funguje ci spis nefunguje tak, ze funkce
WaitForSingleObject ceka do nekonecna a vypocet procedury se
pozastavi, takze se nikdy nedokonci a neuvolni semafor.
V pripade, ze dam misto INFINITE nejakou hodnotu v milisekundach,
tak program 'stoji' po tuto dobu, vypocet se nedokonci a pak se
zacne spoustet podruhe.
Asi na to jdu spatne, nemate nekdo napad, jak to vyresit?
Petr
--------------------
Nokia 3410 za 4.577 Kč. Nejvýhodnější nabídka telefonu na
trhu!http://user.centrum.cz/redir.php?url=http://www.oskarmobil.cz/handsets/handset.php
Odpovedá: Petr Vones
5. 12. 2002 13:37
From: "Petr Selinger" <delphiforum@centrum.cz>
> procedure TForm1.Button1Click(Sender: TObject);
> var i: integer;
> begin
> WaitForSingleObject(Semaphore, INFINITE);
> for i := 0 to 30000 do begin
> Label1.Caption := IntToStr(i);
> Application.ProcessMessages;
> end;
> ReleaseSemaphore(Semaphore, 1, nil);
> end;
>
> procedure TForm1.FormCreate(Sender: TObject);
> begin
> Semaphore := CreateSemaphore(nil, 1, 1, nil);
> end;
>
> procedure TForm1.FormDestroy(Sender: TObject);
> begin
> CloseHandle(Semaphore);
> end;
>
> Jde mi o to, aby kdyz se jiz procedura zpracovava a zavola se znovu,
> aby ke spousteni doslo az po dokonceni prvniho behu procedury.
Ano, protoze tam mas klasickou chybu v podobe Application.ProcessMessages, kde
se ti zevnitr teto metody zavola cokoli, tedy i znovu metoda ve ktere prave
jsi. Znovu musim opakovat, ze v aplikaci ma byt POUZE JEDINE MISTO ve kterem
se zpracovavaji zpravy (a tedy i z neho volaji ruzne casti kodu jako reakce na
tyto zpravy) a tim uz je Application.Run.
Tvuj problem se da resit dvema zpusoby:
1. Volanim Label1.Refresh pro prekresleni, aplikace bude ovsem jinak stale
'mrtva'. Toto tedy neni idealni reseni, ale v nekterych pripadech muze byt
postacujici, hlavne tehdy kdyz GUI aplikace ma zobrazen modalni dialog.
2. Provadeni dane akce dat do samostatneho threadu a hlavni thread aplikace
ponechat jen pro oblsluhu uzivatelskeho rozhrani. Timto problem vyresis
uplne, navic se tak (alespon podle mne) lepe vytvari logika cele aplikace,
protoze umoznuje paralelni beh vice veci. Pomoci VCL action se pak tato
logika i dobre implementuje.
Petr Vones
Odpovedá: Martin Schayna
5. 12. 2002 14:30
----- Original Message -----
From: "Petr Vones" <pvones@mbox.vol.cz>
> Ano, protoze tam mas klasickou chybu v podobe Application.ProcessMessages,
kde
> se ti zevnitr teto metody zavola cokoli, tedy i znovu metoda ve ktere prave
> jsi. Znovu musim opakovat, ze v aplikaci ma byt POUZE JEDINE MISTO ve kterem
> se zpracovavaji zpravy (a tedy i z neho volaji ruzne casti kodu jako reakce
na
> tyto zpravy) a tim uz je Application.Run.
>
> Tvuj problem se da resit dvema zpusoby:
>
> 1. Volanim Label1.Refresh pro prekresleni, aplikace bude ovsem jinak stale
> 'mrtva'. Toto tedy neni idealni reseni, ale v nekterych pripadech muze byt
> postacujici, hlavne tehdy kdyz GUI aplikace ma zobrazen modalni dialog.
Pozor, na Windows XP to uz nestaci, tam pokud se prepne focus na jinou
aplikaci a zpet, okno "zmrzne" a metoda Refresh nic neprekresli do te doby
nez opet zacne aplikace zpracovavat zpravy.
Abych to (neprilis pekne) obesel, musel jsem si misto
Application.ProcessMessages
volat v te smycce svou obsluhu zprav ala ProcessMessages (porusuji tak pravidlo
POUZE JEDINEHO MISTA), avsak pouze pro vlastni okno a pouze pro
zpravu WM_PAINT:
procedure TForm1.ProcessOwnMessages;
var
Msg: TMsg;
begin
while PeekMessage(Msg, Self.Handle, 0, 0, PM_REMOVE) do begin
// budeme zpracovavat pouze vlastni zpravy a to pouze pro prekreslovani
if Msg.Message = WM_PAINT then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
end;
Samozrejme nejspravnejsi je to opravdu delat v threadu.
Martin Schayna
Odpovedá: Viliam Mlich
6. 12. 2002 2:16
Petr Selinger wrote:
> chtel bych, aby se druhy vstup do procedury pozastavil
> a pak az se dokonci prvni, zacne se provadet druhy vstup.
Na taketo cakanie sa nepouzivaju semafory a kriticke sekcie, ale
'fronta'.
Casto robim aplikacie, kde je na RS 485 zbernici niekolko viac-menej
podobnych mikroprocesorov. Komunikuje sa s nimi tak, ze poslem na
zbernicu poziadavku napriklad 'cislo 4, daj mi obsah registra cislo 12'
a zo zbernice pride odpoved 'cislo 4 ma v registri 12 hodnotu xyz'.
Alebo mu len poviem, ze do registra R ma ulozit hodnotu H a on odpovie
'ok'.
V hlavnom threade vznikaju poziadavky tohoto typu nahodile podla
klikania, casovaca atd.. Pri vzniku kazdej poziadavky sa iba zaradi na
koniec fronty record, obsahujuci cislo procesora, text poziadavky a typ
odpovede.
Po zaradeni poziadavky do fronty sa odblokuje thread, ktory ma frontu
obsluzit, ak este neni odblokovany. (V skutocnosti tych zbernic byva
niekolko a kazda ma vlastnu frontu a vlastny thread na vyberanie z nej,
ale zaradovanie sa robi v hlavnom threade).
Thread obsluhy poziadavky prebieha tak, ze sa najprv data odoslu do
portu a po skonceni vysielania sa urcitu dobu caka na odpoved. Nakoniec
sa odpoved (alebo nic, ak timeout alebo badcrc) podla typu odovzda
vhodnej procedure pre spracovanie vysledku a poziadavka z cela fronty sa
odstrani. While not empty.
Preplneniu fronty sa da zabranit obmedzenim jej dlzky pri vkladani alebo
zahadzovanim 'prestarlych' ci podla ineho hladiska menej nutnych
poloziek, podla toho, ake zotavenie z chyby je pre aplikaciu vhodnejsie.
Ja napriklad stanicu, ktory 3x za sebou neodpovie, preventivne
diskvalifikujem na niekolko desiatok sekund.
Kriticke miesto je sucasne pridavanie a vyberanie z fronty. To musi byt
napisane tak, aby kazda operacia mohla byt v lubovolnom (strojovom!)
kroku prerusena a mohla pokracovat druha.
Ja na to nepouzivam retaz, ale obycajne cyklicke pole s ukazovatkom
konca a zaciatku. 'Pridavac' len skontroluje, ci este ma kam a ak ano,
posunie 'koniec'. A 'vyberac' iba posunie 'zaciatok'. Kazdy si pise do
svojej premennej a tu druhu len cita. A ked sa niektora zmeni ihned po
nacitani, tak 'pridavac' mal proste smolu a porovnava nie celkom
najcerstvejsie data, t.j. mysli si, ze uz vo fronte neni miesto a ono sa
medzitym uvolnilo.
Spracovanie odpovede neprebieha v hlavnom threade, preto si treba dat
bacha na volania VCL, najjednoduchsie je len zaradit nieco hlavnemu
threadu do fronty sprav.
bye
vmlich http://www.rar.cz